123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370 |
- using System;
- using System.Collections;
- using System.Linq;
- using System.Reflection;
- using UnityEditor;
- using UnityEditor.SceneManagement;
- using UnityEngine;
- namespace ExternPropertyAttributes.Editor
- {
- public static class ExternalCustomEditorGUI
- {
- public const float IndentLength = 15.0f;
- public const float HorizontalSpacing = 2.0f;
- private static GUIStyle _buttonStyle = new GUIStyle(GUI.skin.button) { richText = true };
- private delegate void PropertyFieldFunction(Rect rect, SerializedProperty property, GUIContent label, bool includeChildren);
- public static void PropertyField(Rect rect, SerializedProperty property, bool includeChildren)
- {
- PropertyField_Implementation(rect, property, includeChildren, DrawPropertyField);
- }
- public static void PropertyField_Layout(SerializedProperty property, bool includeChildren)
- {
- Rect dummyRect = new Rect();
- PropertyField_Implementation(dummyRect, property, includeChildren, DrawPropertyField_Layout);
- }
- private static void DrawPropertyField(Rect rect, SerializedProperty property, GUIContent label, bool includeChildren) {
- EditorGUI.PropertyField(rect, property, label, includeChildren);
- }
- private static void DrawPropertyField_Layout(Rect rect, SerializedProperty property, GUIContent label, bool includeChildren)
- {
- var originalRichText = EditorStyles.label.richText;
- EditorStyles.label.richText = true;
- EditorGUILayout.PropertyField(property, label, includeChildren);
- EditorStyles.label.richText = originalRichText;
- }
- private static void PropertyField_Implementation(Rect rect, SerializedProperty property, bool includeChildren, PropertyFieldFunction propertyFieldFunction)
- {
- var originalRichText = EditorStyles.label.richText;
- EditorStyles.label.richText = true;
-
- SpecialCaseDrawerAttribute specialCaseAttribute = PropertyUtility.GetAttribute<SpecialCaseDrawerAttribute>(property);
- if (specialCaseAttribute != null)
- {
- specialCaseAttribute.GetDrawer().OnGUI(rect, property);
- }
- else
- {
- GUIContent label = PropertyUtility.GetLabel(property);
- bool anyDrawerAttribute = PropertyUtility.GetAttributes<DrawerAttribute>(property).Any();
- if (!anyDrawerAttribute)
- {
- // Drawer attributes check for visibility, enableability and validator themselves,
- // so if a property doesn't have a DrawerAttribute we need to check for these explicitly
- // Check if visible
- bool visible = PropertyUtility.IsVisible(property);
- if (!visible)
- {
- return;
- }
- // Validate
- ValidatorAttribute[] validatorAttributes = PropertyUtility.GetAttributes<ValidatorAttribute>(property);
- foreach (var validatorAttribute in validatorAttributes)
- {
- validatorAttribute.GetValidator().ValidateProperty(property);
- }
- // Check if enabled and draw
- EditorGUI.BeginChangeCheck();
- bool enabled = PropertyUtility.IsEnabled(property);
- using (new EditorGUI.DisabledScope(disabled: !enabled))
- {
- propertyFieldFunction.Invoke(rect, property, label, includeChildren);
- }
- // Call OnValueChanged callbacks
- if (EditorGUI.EndChangeCheck())
- {
- PropertyUtility.CallOnValueChangedCallbacks(property);
- }
- }
- else
- {
- // We don't need to check for enableIfAttribute
- propertyFieldFunction.Invoke(rect, property, label, includeChildren);
- }
- }
-
- EditorStyles.label.richText = originalRichText;
- }
- public static float GetIndentLength(Rect sourceRect)
- {
- Rect indentRect = EditorGUI.IndentedRect(sourceRect);
- float indentLength = indentRect.x - sourceRect.x;
- return indentLength;
- }
- public static void BeginBoxGroup_Layout(string label = "")
- {
- EditorGUILayout.BeginVertical(GUI.skin.box);
- if (!string.IsNullOrEmpty(label))
- {
- EditorGUILayout.LabelField(label, EditorStyles.boldLabel);
- }
- }
- public static void EndBoxGroup_Layout()
- {
- EditorGUILayout.EndVertical();
- }
- /// <summary>
- /// Creates a dropdown
- /// </summary>
- /// <param name="rect">The rect the defines the position and size of the dropdown in the inspector</param>
- /// <param name="serializedObject">The serialized object that is being updated</param>
- /// <param name="target">The target object that contains the dropdown</param>
- /// <param name="dropdownField">The field of the target object that holds the currently selected dropdown value</param>
- /// <param name="label">The label of the dropdown</param>
- /// <param name="selectedValueIndex">The index of the value from the values array</param>
- /// <param name="values">The values of the dropdown</param>
- /// <param name="displayOptions">The display options for the values</param>
- public static void Dropdown(
- Rect rect, SerializedObject serializedObject, object target, FieldInfo dropdownField,
- string label, int selectedValueIndex, object[] values, string[] displayOptions)
- {
- EditorGUI.BeginChangeCheck();
- int newIndex = EditorGUI.Popup(rect, label, selectedValueIndex, displayOptions);
- if (EditorGUI.EndChangeCheck())
- {
- Undo.RecordObject(serializedObject.targetObject, "Dropdown");
- // Problem with structs, because they are value type.
- // The solution is to make boxing/unboxing but unfortunately I don't know the compile time type of the target object
- dropdownField.SetValue(target, values[newIndex]);
- }
- }
- public static void Button(UnityEngine.Object target, MethodInfo methodInfo)
- {
- bool visible = ButtonUtility.IsVisible(target, methodInfo);
- if (!visible)
- {
- return;
- }
- if (methodInfo.GetParameters().All(p => p.IsOptional))
- {
- ButtonAttribute buttonAttribute = (ButtonAttribute)methodInfo.GetCustomAttributes(typeof(ButtonAttribute), true)[0];
- string buttonText = string.IsNullOrEmpty(buttonAttribute.Text) ? ObjectNames.NicifyVariableName(methodInfo.Name) : buttonAttribute.Text;
- bool buttonEnabled = ButtonUtility.IsEnabled(target, methodInfo);
- EButtonEnableMode mode = buttonAttribute.SelectedEnableMode;
- buttonEnabled &=
- mode == EButtonEnableMode.Always ||
- mode == EButtonEnableMode.Editor && !Application.isPlaying ||
- mode == EButtonEnableMode.Playmode && Application.isPlaying;
- bool methodIsCoroutine = methodInfo.ReturnType == typeof(IEnumerator);
- if (methodIsCoroutine)
- {
- buttonEnabled &= (Application.isPlaying ? true : false);
- }
- EditorGUI.BeginDisabledGroup(!buttonEnabled);
- if (GUILayout.Button(buttonText, _buttonStyle))
- {
- object[] defaultParams = methodInfo.GetParameters().Select(p => p.DefaultValue).ToArray();
- IEnumerator methodResult = methodInfo.Invoke(target, defaultParams) as IEnumerator;
- if (!Application.isPlaying)
- {
- // Set target object and scene dirty to serialize changes to disk
- EditorUtility.SetDirty(target);
- PrefabStage stage = PrefabStageUtility.GetCurrentPrefabStage();
- if (stage != null)
- {
- // Prefab mode
- EditorSceneManager.MarkSceneDirty(stage.scene);
- }
- else
- {
- // Normal scene
- EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
- }
- }
- else if (methodResult != null && target is MonoBehaviour behaviour)
- {
- behaviour.StartCoroutine(methodResult);
- }
- }
- EditorGUI.EndDisabledGroup();
- }
- else
- {
- string warning = typeof(ButtonAttribute).Name + " works only on methods with no parameters";
- HelpBox_Layout(warning, MessageType.Warning, context: target, logToConsole: true);
- }
- }
- public static void NativeProperty_Layout(UnityEngine.Object target, PropertyInfo property)
- {
- object value = property.GetValue(target, null);
- if (value == null)
- {
- string warning = string.Format("{0} is null. {1} doesn't support reference types with null value", property.Name, typeof(ShowNativePropertyAttribute).Name);
- HelpBox_Layout(warning, MessageType.Warning, context: target);
- }
- else if (!Field_Layout(value, property.Name))
- {
- string warning = string.Format("{0} doesn't support {1} types", typeof(ShowNativePropertyAttribute).Name, property.PropertyType.Name);
- HelpBox_Layout(warning, MessageType.Warning, context: target);
- }
- }
- public static void NonSerializedField_Layout(UnityEngine.Object target, FieldInfo field)
- {
- object value = field.GetValue(target);
- if (value == null)
- {
- string warning = string.Format("{0} is null. {1} doesn't support reference types with null value", field.Name, typeof(ShowNonSerializedFieldAttribute).Name);
- HelpBox_Layout(warning, MessageType.Warning, context: target);
- }
- else if (!Field_Layout(value, field.Name))
- {
- string warning = string.Format("{0} doesn't support {1} types", typeof(ShowNonSerializedFieldAttribute).Name, field.FieldType.Name);
- HelpBox_Layout(warning, MessageType.Warning, context: target);
- }
- }
- public static void HorizontalLine(Rect rect, float height, Color color)
- {
- rect.height = height;
- EditorGUI.DrawRect(rect, color);
- }
- public static void HelpBox(Rect rect, string message, MessageType type, UnityEngine.Object context = null, bool logToConsole = false)
- {
- EditorGUI.HelpBox(rect, message, type);
- if (logToConsole)
- {
- DebugLogMessage(message, type, context);
- }
- }
- public static void HelpBox_Layout(string message, MessageType type, UnityEngine.Object context = null, bool logToConsole = false)
- {
- EditorGUILayout.HelpBox(message, type);
- if (logToConsole)
- {
- DebugLogMessage(message, type, context);
- }
- }
- public static bool Field_Layout(object value, string label)
- {
- using (new EditorGUI.DisabledScope(disabled: true))
- {
- bool isDrawn = true;
- Type valueType = value.GetType();
- if (valueType == typeof(bool))
- {
- EditorGUILayout.Toggle(label, (bool)value);
- }
- else if (valueType == typeof(int))
- {
- EditorGUILayout.IntField(label, (int)value);
- }
- else if (valueType == typeof(long))
- {
- EditorGUILayout.LongField(label, (long)value);
- }
- else if (valueType == typeof(float))
- {
- EditorGUILayout.FloatField(label, (float)value);
- }
- else if (valueType == typeof(double))
- {
- EditorGUILayout.DoubleField(label, (double)value);
- }
- else if (valueType == typeof(string))
- {
- EditorGUILayout.TextField(label, (string)value);
- }
- else if (valueType == typeof(Vector2))
- {
- EditorGUILayout.Vector2Field(label, (Vector2)value);
- }
- else if (valueType == typeof(Vector3))
- {
- EditorGUILayout.Vector3Field(label, (Vector3)value);
- }
- else if (valueType == typeof(Vector4))
- {
- EditorGUILayout.Vector4Field(label, (Vector4)value);
- }
- else if (valueType == typeof(Color))
- {
- EditorGUILayout.ColorField(label, (Color)value);
- }
- else if (valueType == typeof(Bounds))
- {
- EditorGUILayout.BoundsField(label, (Bounds)value);
- }
- else if (valueType == typeof(Rect))
- {
- EditorGUILayout.RectField(label, (Rect)value);
- }
- else if (typeof(UnityEngine.Object).IsAssignableFrom(valueType))
- {
- EditorGUILayout.ObjectField(label, (UnityEngine.Object)value, valueType, true);
- }
- else if (valueType.BaseType == typeof(Enum))
- {
- EditorGUILayout.EnumPopup(label, (Enum)value);
- }
- else if (valueType.BaseType == typeof(System.Reflection.TypeInfo))
- {
- EditorGUILayout.TextField(label, value.ToString());
- }
- else
- {
- isDrawn = false;
- }
- return isDrawn;
- }
- }
- private static void DebugLogMessage(string message, MessageType type, UnityEngine.Object context)
- {
- switch (type)
- {
- case MessageType.None:
- case MessageType.Info:
- Debug.Log(message, context);
- break;
- case MessageType.Warning:
- Debug.LogWarning(message, context);
- break;
- case MessageType.Error:
- Debug.LogError(message, context);
- break;
- }
- }
- }
- }
|